import tqdm
import random

from core.linalg import Cord2D


class Cell:
    def __init__(self, mutable: bool = True, initial_value: int = 0):
        self.mutable = mutable
        self.value   = initial_value


class CircleCell(Cell):
    def __init__(self, radius: int, mutable: bool = True, initial_value: int = 0):
        super().__init__(mutable, initial_value)
        self.radius = radius

    def get_raw_depended_cords(self, center: Cord2D) -> list[Cord2D]:
        result = []
        for raw_x in range(-self.radius, self.radius + 1):
            for raw_y in range(-self.radius, self.radius + 1):
                if raw_x**2 + raw_y**2 <= self.radius**2:
                    result.append(Cord2D(raw_x + center.x, raw_y + center.y))
        return result


class CellBasedNoiseGenerator:
    def __init__(self, width: int = 256, height: int = 256):
        self.width  = width
        self.height = height
        self.cells  = {}

    def clear_world(self):
        self.cells.clear()

    def shuffle_world(self, density: float, scale: int):
        # step 0: handle args
        density = int(density * self.width * self.height)

        # step 1: clear the world
        self.clear_world()

        # step 2: place circle cells

        for _ in tqdm.tqdm(range(density), desc='Shuffling World...'):
            cell_pos = Cord2D(
                x = random.randint(0, self.height - 1),
                y = random.randint(0, self.width  - 1),
            )
            self.cells[cell_pos] = CircleCell(
                radius        = random.randint(1, scale),
                mutable       = False,
                initial_value = 0
            )

    def __next__(self):
        next_cells = self.cells.copy()
        for cell_cords in tqdm.tqdm(self.cells, desc='Iterating Cells...'):
            cell = self.cells[cell_cords]
            if isinstance(cell, CircleCell):
                for child_cords in cell.get_raw_depended_cords(cell_cords):
                    if child_cords not in self.cells:
                        next_cells[child_cords] = Cell(mutable = True, initial_value = cell.value)
        self.cells = next_cells

    def __repr__(self) -> str:
        return f'{self.__class__.__name__}(width={self.width}, height={self.height})'
